import * as crypto from 'crypto';
import { ulid as generateUlid } from "ulid";
import { formatDates } from '../common/utils';

// 加密算法配置
const algorithm = 'aes-256-cbc';
const secret = process.env.ENCRYPT_SECRET;
// 生成固定长度的密钥与IV（向量）
function generateKeyAndIv(secret: string) {
  const key = crypto
    .createHash('sha256')
    .update(String(secret))
    .digest()
    .subarray(0, 32);
  const iv = crypto
    .createHash('sha256')
    .update(String(secret))
    .digest()
    .subarray(0, 16);
  return { key, iv };
}
export function encrypt(text: string = ''): string {
  const { key, iv } = generateKeyAndIv(secret);
  const cipher = crypto.createCipheriv(algorithm, key, iv);
  let encrypted = cipher.update(text, 'utf8', 'hex');
  encrypted += cipher.final('hex');
  return encrypted;
}
export function decrypt(text: string = ''): string {
  const { key, iv } = generateKeyAndIv(secret);
  const decipher = crypto.createDecipheriv(algorithm, key, iv);
  let decrypted = decipher.update(text, 'hex', 'utf8');
  decrypted += decipher.final('utf8');
  return decrypted;
}
/**
 * 用户id是否是管理员
 *
 * @param {string} user_id
 * @return {*}
 */
export const isAdmin = (user_id: string): boolean => {
  const adminIds = (process.env.ADMIN_IDS || '').split(',').filter(Boolean);
  return adminIds.includes(user_id);
};

/**
 * md5加密
 *
 * @param {string} data
 * @return {*}  {string}
 */
export const md5 = (data: string): string => {
  const hash = crypto.createHash('md5');
  hash.update(data);
  return hash.digest('hex');
};

/**
 * 计算当前时间到24点还有多少秒
 *
 * @return {*} number 秒
 */
export const toEndTodaySeconds = (): number => {
  const now = new Date();
  const midnight = new Date(now);
  midnight.setHours(24, 0, 0, 0); // 设置为今天的24:00

  const seconds = Math.floor((midnight.getTime() - now.getTime()) / 1000);
  return seconds;
};

/**
 * 生成随机长度字符串
 *
 * @param {number} [length=6] 字符串长度
 * @param {boolean} [withTime=true] 是否携带时间戳
 * @return {*} 随机字符串
 */
export const getRandomStr = (
  length: number = 6,
  withTime: boolean = false,
): string => {
  const characters =
    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
  const charactersLength = characters.length;
  const now = +new Date();
  let result = '';

  const randomBytes = crypto.randomBytes(length);

  for (let i = 0; i < length; i++) {
    result += characters[randomBytes[i] % charactersLength];
  }

  return `${withTime ? now : ''}${result}`;
};

/**
 * 生成redis key
 *
 * @param {string} prefix redis key前缀
 * @param {string} type 类型
 * @param {string} suffix redis key后缀
 *
 * @return {*} string redis key
 */
export const getRedisKey = (
  prefix: string,
  type: string,
  suffix: string,
): string => {
  return `${prefix}_${type}_${suffix}`;
};

/**
 * 生成model的slug
 *
 * @param {string} type 类型，drawing，chat
 * @param {string} model 模型
 * @param {string} [mode] 模式，比如mj的relax，fast，turbo
 * @param {string} [version] 版本，比如mj的v6.1,v7
 * @return {*}
 */
export const getSlug = (
  type: string,
  model: string,
  mode?: string,
  version?: string,
): string => {
  return `${type || ''}_${model || ''}${mode ? `_${mode}` : ''}${version ? `_${version}` : ''}`;
};

/**
 * 获取用户初始化头像
 *
 * @return {*} 头像URL
 */
export const getAvatarUrl = (): string => {
  const styles = {
    adventurer: {
      seed: ['Felix', 'Aneka'],
      backgroundColor: ['b6e3f4', 'c0aede', 'd1d4f9', 'ffd5dc', 'ffdfbf'],
      eyes: [
        'variant01',
        'variant03',
        'variant05',
        'variant08',
        'variant19',
        'variant21',
        'variant23',
        'variant26',
      ],
      glasses: [
        'variant01',
        'variant02',
        'variant03',
        'variant04',
        'variant05',
      ],
      hair: new Array(26)
        .fill(0)
        .map((i) => `long${i < 10 ? `0${i + 1}` : i + 1}`)
        .concat(
          new Array(19)
            .fill(0)
            .map((i) => `short${i < 10 ? `0${i + 1}` : i + 1}`),
        ),
      hairColor: [
        '0e0e0e',
        '3eac2c',
        '6a4e35',
        '85c2c6',
        '796a45',
        '562306',
        '592454',
        'ac6511',
        'afafaf',
        'b9a05f',
        'cb6820',
        'dba3be',
        'e5d7a3',
        'ab2a18',
      ],
      mouth: new Array(30)
        .fill(0)
        .map((i) => `variant${i < 10 ? `0${i + 1}` : i + 1}`),
    },
    'adventurer-neutral': {
      seed: ['Felix', 'Aneka'],
      backgroundColor: ['b6e3f4', 'c0aede', 'd1d4f9', 'ffd5dc', 'ffdfbf'],
      eyes: [
        'variant01',
        'variant04',
        'variant07',
        'variant09',
        'variant12',
        'variant15',
        'variant16',
        'variant19',
        'variant21',
        'variant23',
        'variant24',
        'variant26',
      ],
      glasses: [
        'variant01',
        'variant02',
        'variant03',
        'variant04',
        'variant05',
      ],
      mouth: [
        'variant01',
        'variant02',
        'variant03',
        'variant04',
        'variant07',
        'variant09',
        'variant10',
        'variant12',
        'variant13',
        'variant14',
        'variant15',
        'variant17',
        'variant20',
        'variant25',
        'variant26',
        'variant27',
        'variant28',
        'variant29',
        'variant30',
      ],
    },
    avataaars: {
      seed: ['Felix', 'Aneka'],
      backgroundColor: ['b6e3f4', 'c0aede', 'd1d4f9', 'ffd5dc', 'ffdfbf'],
      top: [
        'bigHair',
        'curly',
        'dreads01',
        'frizzle',
        'hat',
        'shaggy',
        'shortCurly',
        'turban',
      ],
    },
    'avataaars-neutral': {
      seed: ['Felix', 'Aneka'],
      backgroundColor: ['b6e3f4', 'c0aede', 'd1d4f9', 'ffd5dc', 'ffdfbf'],
      eyes: [
        'closed',
        'cry',
        'default',
        'eyeRoll',
        'happy',
        'hearts',
        'side',
        'squint',
        'wink',
        'winkWacky',
        'xDizzy',
      ],
      mouth: ['concerned', 'eating', 'grimace', 'screamOpen', 'vomit'],
    },
    'big-ears-neutral': {
      seed: ['Felix', 'Aneka'],
      backgroundColor: ['b6e3f4', 'c0aede', 'd1d4f9', 'ffd5dc', 'ffdfbf'],
      cheek: [
        'variant01',
        'variant02',
        'variant03',
        'variant04',
        'variant05',
        'variant06',
      ],
    },
    bottts: {
      seed: ['Felix', 'Aneka'],
      backgroundColor: ['b6e3f4', 'c0aede', 'd1d4f9', 'ffd5dc', 'ffdfbf'],
    },
    croodles: {
      seed: ['Felix', 'Aneka'],
      backgroundColor: ['b6e3f4', 'c0aede', 'd1d4f9', 'ffd5dc', 'ffdfbf'],
      eyes: [
        'variant01',
        'variant02',
        'variant04',
        'variant07',
        'variant09',
        'variant14',
        'variant15',
      ],
    },
    dylan: {
      seed: ['Felix', 'Aneka'],
      backgroundColor: ['b6e3f4', 'c0aede', 'd1d4f9', 'ffd5dc', 'ffdfbf'],
      hair: [
        'bangs',
        'buns',
        'flatTop',
        'fluffy',
        'longCurls',
        'parting',
        'roundBob',
        'shaggy',
        'shortCurls',
        'spiky',
        'wavy',
      ],
    },
    'fun-emoji': {
      seed: ['Felix', 'Aneka'],
      backgroundColor: ['b6e3f4', 'c0aede', 'd1d4f9', 'ffd5dc', 'ffdfbf'],
      eyes: [
        'closed',
        'closed2',
        'crying',
        'cute',
        'glasses',
        'love',
        'pissed',
        'plain',
        'sad',
        'shades',
        'stars',
        'tearDrop',
        'wink',
        'wink2',
      ],
      mouth: [
        'cute',
        'drip',
        'faceMask',
        'kissHeart',
        'shout',
        'sick',
        'smileLol',
      ],
    },
    lorelei: {
      seed: ['Felix', 'Aneka'],
      backgroundColor: ['b6e3f4', 'c0aede', 'd1d4f9', 'ffd5dc', 'ffdfbf'],
      hair: [
        'variant03',
        'variant04',
        'variant07',
        'variant10',
        'variant11',
        'variant14',
        'variant16',
        'variant20',
        'variant25',
        'variant27',
        'variant29',
        'variant31',
        'variant32',
        'variant33',
        'variant35',
        'variant38',
        'variant41',
        'variant42',
        'variant45',
      ],
    },
    micah: {
      seed: ['Felix', 'Aneka'],
      backgroundColor: ['b6e3f4', 'c0aede', 'd1d4f9', 'ffd5dc', 'ffdfbf'],
      glasses: ['round', 'square'],
      hair: ['dannyPhantom', 'fonze', 'full', 'mrClean', 'pixie'],
      hairColor: ['000000', '6bd9e9', 'ac6651', 'd2eff3', 'fc909f', 'ffedef'],
      mouth: [
        'frown',
        'laughing',
        'nervous',
        'pucker',
        'sad',
        'smile',
        'smirk',
        'surprised',
      ],
      shirtColor: [
        '6bd9e9',
        '9287ff',
        'ac6651',
        'd2eff3',
        'f4d150',
        'fc909f',
        'ffeba4',
      ],
    },
    miniavs: {
      hair: ['balndess', 'classic01', 'elvis', 'ponyTail'],
      mustache: ['freddy', 'horshoe', 'pencilThin', 'pencilThinBeard'],
    },
    notionists: {
      seed: ['Felix', 'Aneka'],
      body: [
        'variant01',
        'variant02',
        'variant05',
        'variant06',
        'variant10',
        'variant13',
        'variant15',
        'variant20',
        'variant22',
        'variant25',
      ],
      gesture: [
        'hand',
        'handPhone',
        'okLongArm',
        'pointLongArm',
        'waveLongArm',
        'waveLongArms',
        'waveOkLongArms',
        'wavePointLongArms',
      ],
      hair: [
        'hat',
        'variant01variant04',
        'variant05',
        'variant07',
        'variant08',
        'variant11',
        'variant12',
        'variant18',
        'variant26',
        'variant27',
        'variant39',
        'variant45',
        'variant46',
        'variant57',
      ],
    },
    personas: {
      clothingColor: [
        '6dbb58',
        '54d7c7',
        '456dff',
        '7555ca',
        'e24553',
        'f3b63a',
        'f55d81',
      ],
      eyes: ['glasses', 'happy', 'open', 'sleep', 'sunglasses', 'wink'],
    },
    'pixel-art': {
      seed: ['Felix', 'Aneka'],
    },
  };
  const keys = Object.keys(styles);
  const random = Math.floor(Math.random() * keys.length); // [0, 15) -> [0, 14]
  const key = keys[random];
  const obj = styles[key];

  let query = '';
  for (const k in obj) {
    const arr = obj[k];
    const index = Math.floor(Math.random() * arr.length);
    query += `&${k}=${arr[index]}`;
  }

  const url = `https://api.dicebear.com/9.x/${key}/png?${query.substring(1)}`;

  return url;
};

/**
 * 随机生成用户昵称
 *
 * @return {*} 随机昵称
 */
export const getRandomName = ():string => {
  const wordList = ['雪', '晴', '欣', '萌', '萱', '怡', '悦', '玲', '岚', '瑶', '明', '天', '文', '宇', '涵', '睿', '轩', '霖', '辰', '浩', '星', '月', '夜', '风', '云', '雨', '霜'];
  const index = Math.floor(Math.random() * wordList.length);
  const word = wordList[index] || wordList[0];
  return word.repeat(2);
}

/**
 * 获取数字
 *
 * @param {*} input 需要转换的数字、字符串
 * @param {boolean} [isRound=true] 是否要返回整数，默认返回整数
 * @return {*}
 */
export const toNumber = (input: any, isRound: boolean = true): any => {
  // 检查输入是否为数字
  if (typeof input === 'number') return input;

  // 如果输入是字符串，尝试将其转换为数字
  if (typeof input === 'string') {
    const parsed = isRound ? parseInt(input) : parseFloat(input);
    // 检查解析后的值是否是有效的数字
    return isNaN(parsed) ? 0 : parsed;
  }

  return 0;
};

/**
 * 用户信息脱敏，手机号，密码
 *
 * @param {*} [userInfo={}]
 * @return {*} userInfo
 */
export const safeOutput = (userInfo: any = {}): any => {
  const pe = {};
  if (userInfo?.phone) pe['phone'] = maskMobile(decrypt(userInfo.phone));
  if (userInfo?.email) pe['email'] = maskEmail(decrypt(userInfo.email));
  if (userInfo?.created_at) pe['created_at'] = formatDates(userInfo.created_at);
  if (userInfo?.updated_at) pe['updated_at'] = formatDates(userInfo.updated_at);
  if (userInfo?.deleted_at) pe['deleted_at'] = formatDates(userInfo.deleted_at);

  const password = !!userInfo.password;

  return { ...userInfo, password, ...pe };
};

/**
 * 判断字符串是否是邮箱
 *
 * @param {string} [str='']
 * @return {*} boolean
 */
export const validateEmail = (str: string = ''): boolean =>
  /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/.test(str);

/**
 * 判断字符串是否是手机
 *
 * @param {string} [str='']
 * @return {*} boolean
 */
export const validatePhone = (str: string = ''): boolean =>
  /^((13[0-9])|(14[0-9])|(15([0-9]))|(16[0-9])|(17[0-9])|(18[0-9])|(19[0-9]))\d{8}$/.test(
    str,
  );

/**
 * 数组转tree结构
 *
 * @param {any[]} nodes
 * @param {number} [root_id=0]
 * @return {*}  {any[]}
 */
export const array2Tree = (nodes: any[], root_id: number = 0): any[] => {
  const map = new Map<number, any>();
  const roots: any[] = [];

  // 初始化map，每个节点都加入到map中
  nodes.forEach((node) => map.set(node.id, { ...node, children: [] }));

  // 第二次遍历，根据pid找到父节点，并把当前节点作为父节点的子节点
  map.forEach((node) => {
    if (root_id === node.pid) {
      // 如果没有父节点，则认为是根节点
      roots.push(node);
    } else {
      // 否则，找到其父节点并添加到父节点的children列表中
      const parent = map.get(node.pid);
      if (parent) {
        parent.children.push(node);
      }
    }
  });

  return roots;
};

/**
 * 手机脱敏
 *
 * @param {string} phone 需要脱敏的手机号
 * @return {*}  {string}
 */
export const maskMobile = (phone: string): string => {
  if (!phone) return '';
  return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
};
/**
 * 邮箱脱敏
 *
 * @param {string} email 需要脱敏的邮箱
 * @return {*}  {string}
 */
export const maskEmail = (email: string): string => {
  if (!email) return '';
  const parts = email.split('@');
  const username = parts[0].slice(0, 1) + '*'.repeat(6);
  return `${username}@${parts[1]}`;
};


/**
 * 生成ulid
 *
 * @return {*}  {string}
 */
export const ulid = ():string => {
  return generateUlid().toLowerCase();
}